package com.curluin.controll;

import java.util.HashMap;
import java.util.Timer;
import java.util.Vector;

import com.curluin.approximateShapes.ApproximateShape;
import com.curluin.fileio.PlyFileReader;
import com.curluin.inputInterpretation.SpinInterpreter;
import com.curluin.inputInterpretation.TiltEvent;
import com.curluin.inputInterpretation.TiltInterpreter;
import com.curluin.inputInterpretation.TouchEvent;
import com.curluin.inputInterpretation.TouchPoint;
import com.curluin.math.MyVector;
import com.curluin.model.Fishbone;
import com.curluin.model.Goal;
import com.curluin.model.GridField;
import com.curluin.model.Ground;
import com.curluin.model.ModelObject;
import com.curluin.model.Moveable;
import com.curluin.model.Obstacle;
import com.curluin.model.Pinguin;
import com.curluin.model.Snow;
import com.curluin.model.Water;
import com.curluin.scene.Node;
import com.curluin.scene.TriangleMesh;
import com.curluin.view.Camera;
import com.curluin.view.CameraController;
import com.curluin.view.CameraRailCheckPoint;
import com.curluin.view.Interactor;

import android.os.Bundle;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.util.Log;

import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class Controller extends Activity implements OnClickListener{

	public static final String LOG_TAG="Controller";
	
	/**
	 * Show GridFields
	 */
	public static boolean DEBUG_MODE = false;	
	
	public static boolean SCENE_ROTATABLE = false;

	/**
	 * Gravity acceleration per timerintervall
	 */
	public static final float GRAVITY_ACCELERATION = -0.001f;


	/**
	 * While gamephase is not defined
	 */
	public static final int GAMEPHASE_UNDEFINED = -1;
	
	/**
	 * While Overview of level is shown in a cameratour
	 */
	public static final int GAMEPHASE_CAMERATOUR = 0;
	
	/**
	 * While Pinguin is located on Runway
	 */
	public static final int GAMEPHASE_RUNWAY = 1;

	/**
	 * While Pinguin is located on Rink
	 */
	public static final int GAMEPHASE_RINK = 2;

	/**
	 * While Pinguin is in finishing phase
	 */
	public static final int GAMEPHASE_FINISH = 3;

	/**
	 * While the underlying Ground of Pinguin is Water
	 */
	public static final int GAMEPHASE_WATER = 4;

	/**
	 * Interval of execution of TimerAction (= refreshing interval)
	 */
	public static final int TIMER_INTERVAL = 40;

	/**
	 * Minimal velocity, if Pinuin is slower game will end
	 */
	public static final float minVelocity = 0.1f/TIMER_INTERVAL;

	public static float maximalJumpVelocity = 1.2f/TIMER_INTERVAL;

	/**
	 * max acceleration per wipe
	 */
	public static final float maxAcceleration = 1f/TIMER_INTERVAL;
	/**
	 * estimated minimal duration (in ms) possible for wipes
	 */
	public static final  float minWipeDuration = 40; 

	/**
	 * states if the last Touch was on the left side, needed for checking if wipes in Gamephase RUNWAY are alternating 
	 */
	private boolean lastWipeLeft;

	/**
	 * Phase of game
	 */
	public static int gamePhase = GAMEPHASE_UNDEFINED;

	/**
	 * This member contains the 3D view.
	 */
	private Interactor interactor = null;

	/**
	 * Application context. Can be fetched by all classes by 'getAppContext()'
	 */
	private static Context context;

	/**
	 *  Origin of coords which should be covered by GridFields
	 */
	public static final float xModelOrigin=0,zModelOrigin=0;
	/**
	 *  Absolut value for max coords which should be covered by GridFields
	 */
	public static float xModelRange, zModelRange;


	// Max coords
	public static float visibleWidth;
	public static float visibleLength;

	/**
	 * Only for debugging, touched GridFields of previous wipe
	 */
	private static Vector<GridField> touchedGFs;

	/**
	 * number of GridFields in x-direction
	 */
	private static final int numGridFields = 30;

	/**
	 * Representation of the 3d visualisation space via GridFields
	 */
	private GridField[][] grid;


	private Vector<ModelObject> objects;


	/**
	 * Level to play
	 */
	private int level;
	/**
	 * Goal pinguin has to achieve
	 */
	private Goal goal;
	/**
	 * Pinguin
	 */
	public Pinguin pinguin;



	private Timer timer=null;

	private CameraController cameraController;

	/**
	 * Root node which holds the whole scene graph
	 */
	private Node rootNode;


	/**
	 * Creates Sounds
	 */
	private JukeBox jukeBox;

	/**
	 * Responsible for tilt interpretation
	 */
	private TiltInterpreter tiltInterpreter;

	/**
	 * Responsible for spin interpretation
	 */
	private SpinInterpreter spinInterpreter;


	/**
	 * Objects which should be removed in next recompution step
	 */
	private Vector<ModelObject> objectsToRemove=new Vector<ModelObject>();
	/**
	 * GameOverd-Dialog and its buttons
	 */
	private Dialog dialog;
	private Button restartButton,menuButton;


	/**
	 * True only for first initialization, important so that onresume does no harm
	 */
	private boolean firstInitialization;

	//	private boolean pauseRecompution;
	/**
	 * for option-menu, states if onResume() should be called after optionmenu was closed
	 */
	private boolean resume=true;
	/**
	 * states if the next recompution (recomputeModel()) will be the first Recompution
	 */
	private boolean firstRecompution;
	/**
	 * states if model should be recomputed
	 */
	public boolean doRecompute=true;


	/**
	 * Event-Handler: application is created.
	 * 
	 * @see android.app.Activity#onCreate(android.os.Bundle)
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.e(LOG_TAG, "OnCreate");
		context = getApplicationContext();

		firstInitialization=true;
		firstRecompution=true;
	

		//initialize Sounds
		jukeBox=new JukeBox(this);

		//initialize game over dialog
		initializeGameOverDialog();

		// Remove title bar
		this.requestWindowFeature(Window.FEATURE_NO_TITLE);

		// Remove notification bar
		this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

		//set up Visuialization
		interactor = new Interactor(this);
		this.cameraController=interactor.cameraController;

		// Load level
		level = getIntent().getExtras().getInt("levelNumber",0);

		objects = loadLevel(level);

		interactor.setScene(rootNode);

		setContentView(interactor);

		// Start a Tour to view the whole level
		timer = new Timer();
		gamePhase = GAMEPHASE_CAMERATOUR;
		int tourDuration=10000;
		int tourWaitOnEnd=2000;
		MyVector tourStartPoint=MyVector.makeVector3(pinguin.getCenter().get(0), 0, pinguin.getCenter().get(2)-pinguin.getSize().get(1));//pinguin.position.subtract(MyVector.makeVector3(0, 0, -pinguin.position.get(2)+15));//pinguin.getSize().get(2)));
		Vector<CameraRailCheckPoint> tour= new Vector<CameraRailCheckPoint>();
		tour.add(new CameraRailCheckPoint(tourStartPoint, MyVector.makeVector3(0, 0, 0), Camera.HEIGHT_DEFAULT));
		tour.add(new CameraRailCheckPoint(goal.getTargetPosition(), MyVector.makeVector3(0, 0, 0), Camera.HEIGHT_DEFAULT));
		
		//set Rail for Camera
		MyVector railStartPoint=MyVector.makeVector3(pinguin.getCenter().get(0), 0, pinguin.getCenter().get(2)-pinguin.getSize().get(1));//pinguin.position.subtract(MyVector.makeVector3(0, 0, -pinguin.position.get(2)+15));//pinguin.getSize().get(2)));
		Vector<CameraRailCheckPoint> rail= new Vector<CameraRailCheckPoint>();
		rail.add(new CameraRailCheckPoint(railStartPoint, MyVector.makeVector3(0, 0, 0), Camera.HEIGHT_DEFAULT));
		rail.add(new CameraRailCheckPoint(goal.getTargetPosition(), MyVector.makeVector3(0, 0, 0), Camera.HEIGHT_DEFAULT));
		cameraController.setRailTrack(rail);
		
		if(!SCENE_ROTATABLE) cameraController.cameraTour(tour,tourDuration,timer, TIMER_INTERVAL);
		else cameraController.moveCameraOnRail(0.001f);

		//start timer

		timer.scheduleAtFixedRate(new TimerAction(this), tourDuration+tourWaitOnEnd , TIMER_INTERVAL);

		tiltInterpreter= new TiltInterpreter(this);
		spinInterpreter = new SpinInterpreter(this);

	}

	public static Context getAppContext(){
		return context;
	}

	private void initializeGameOverDialog(){
		Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/DESTROY.ttf");
		dialog = new Dialog(Controller.this,android.R.style.Theme_Translucent_NoTitleBar_Fullscreen);
		dialog.setContentView(R.layout.gameoverlayout);
		dialog.setCancelable(false);
		menuButton = (Button) dialog.findViewById(R.id.bMainMenu);
		menuButton.setTypeface(tf);
		restartButton = (Button) dialog.findViewById(R.id.bRestart);
		restartButton.setTypeface(tf);
		menuButton.setOnClickListener(this);
		restartButton.setOnClickListener(this);
		TextView tview= (TextView) dialog.findViewById(R.id.textHighScore);
		tview.setTypeface(tf);
		tview= (TextView) dialog.findViewById(R.id.textGameResult);
		tview.setTypeface(tf);
	}

	/**Handle collisions of a ModelObject
	 * @param mo1
	 */
	public void handleCollisions(Moveable mo) {

		HashMap<ModelObject, MyVector> collisions = mo.getCollisions();
		boolean fatal=false;
		for(ModelObject o: collisions.keySet()){
			Log.e(LOG_TAG,"Collision with: "+o);
			fatal|=animateAndComputeCollision(mo,o,collisions.get(o));
			jukeBox.playCollisionSound(o.getConsitency(),mo.getConsitency());
		}

		if(fatal){
			if(mo instanceof Pinguin){//fatal collision for pinguin occurred, end game
				Log.e(LOG_TAG, "PENGUIN COLLIDED FATALLY");
				endGameRun(true);
			}
			else {//fatally collided non-pinguin moveable Objects will be removed from game
				mo.removeFromGridFields();
				objectsToRemove.add(mo);
			}

		}
		mo.removeCollisions();
	}

	/**
	 * Ends this game run. Stops timer and shows Screen which either shows score or simply GameOver
	 * @param lost true if pinguin crashed
	 */
	private void endGameRun(boolean lost) {
		Log.e(LOG_TAG,"GAME ENDED");
		onPause();
		//start camera tour to pinguin
		cameraController.unlockAllCameraDimensions();
		Vector<CameraRailCheckPoint> endTour = new Vector<CameraRailCheckPoint>();
		endTour.add(new CameraRailCheckPoint(MyVector.makeCopy(cameraController.getRef()), new MyVector(3), cameraController.getEye().get(1)));
		endTour.add(new CameraRailCheckPoint(pinguin.getCenter(), new MyVector(3), Camera.HEIGHT_FINISH));
		cameraController.cameraTour(endTour, 1000, timer, TIMER_INTERVAL);


		//compute score if the game was not lost (e.g. pengin collided) 
		int score=0;
		if(!lost){
			score = goal.computeScore(pinguin.position);
			compareAndWriteHighscore(score);
		}

		//set dialog text
		setGameOverDialogText(lost,score);

		//show dialog
		runOnUiThread(new Runnable(){
			@Override
			public void run()
			{ 
				dialog.show();
			}
		}
				);

	}

	private void setGameOverDialogText( boolean lost, int score){
		TextView text=(TextView)dialog.findViewById(R.id.textGameResult);
		text.setText(lost? "GAME OVER":"YOUR SCORE "+String.valueOf(score));
		TextView txtHighscore=(TextView)dialog.findViewById(R.id.textHighScore);
		txtHighscore.setText("HIGHSCORE "+readHighscore());
	}

	/**
	 * Only dummy for real Collision handling, should be done by a physic class. 
	 * Nethertheless should return if the collision was fatal for the object (could not be fatal if Objects can bounce away from other Obstacles). 
	 * Fatal means for Pinguin that game ends, for Obstacle that it should disappear out of game.
	 * @param mo1
	 * @param mo2
	 * @param location
	 * @return 
	 */
	private boolean animateAndComputeCollision(Moveable mo1, ModelObject mo2, MyVector location) {
		// TODO Fill with sense, should be done by Physic-engine
		boolean fatal=false;
		if(mo1 instanceof Pinguin) fatal= true;
		if(mo2 instanceof Water||mo2 instanceof Ground){
			Log.w(LOG_TAG, "Fall into Water");
			fatal= true;
		}
		if(mo1 instanceof Obstacle){
			mo1.setVelocity(MyVector.makeVector3(0, 0, 0));
		}
		return fatal;
	}

	/**
	 * Handles touch event depending on game phase
	 * @param touchEvent
	 */
	public void handleTouchEvent(TouchEvent touchEvent){
		TouchPoint touchPoint = touchEvent.getLastTouchPoint();
		MyVector openGlTouch = touchPoint.getOpenGlPosition(interactor.camera);
		switch (gamePhase){
		case GAMEPHASE_CAMERATOUR:{
			switch (touchEvent.state){
			case TouchEvent.TOUCH_FINISHED:
				//cancel cameratour and  all other tasks of timer
				timer.cancel();
				timer.purge();
				//start game 
				timer = new Timer();
				timer.scheduleAtFixedRate(new TimerAction(this), 0 , TIMER_INTERVAL);
				break;
			}
			break;
		}
		case GAMEPHASE_RUNWAY:{
			boolean wipeLeft = touchEvent.getAverageSide()<0;
			//check if wipes are alternating, if not do nothing
			if(wipeLeft==lastWipeLeft) break;
			switch (touchEvent.state){
			case TouchEvent.TOUCH_STARTED:{
				debugTouchStarted(openGlTouch);
				break;
			}
			case TouchEvent.TOUCH_MOVING:{
				MyVector secondLast = touchEvent.getSecondLastTouchPoint().getOpenGlPosition(interactor.camera);
				informTouchedGridField(getGridFieldLine(secondLast, openGlTouch));
				//check if wipe is from upper end to lower end
				if(touchEvent.getVelocity(true).get(1)>0){
					float rotationAngle = touchPoint.offset.getNorm()*Pinguin.MAX_DIRECT_ANGLE*touchEvent.getAverageSide();
					pinguin.direct(rotationAngle);
					//					Log.i(LOG_TAG, "rotAngle: "+rotationAngle);
				}
				break;
			}
			case TouchEvent.TOUCH_FINISHED:{
				debugTouchFinished();

				//				Log.e(LOG_TAG, "Length "+touchEvent.getLength()+" duration "+touchEvent.getDuration() );
				//accelerate in respect to duration of wipe
				if(touchEvent.getVelocity(true).get(1)>0){//check if wipe is from upper end to lower end

					float acc = minWipeDuration/touchEvent.getDuration();
					acc*=maxAcceleration;
					//accelerate pinguin
					pinguin.accelerate(acc);
					//wiggle (animate step of) penguin 
					pinguin.wiggle(wipeLeft);
					//remember side of last Wipe (for checking if wipes are alternating in side)
					lastWipeLeft = wipeLeft;
				}

				break;
			}
			default: Log.e(LOG_TAG, "Bad touch event state!");
			}
			break;
		}
		case GAMEPHASE_RINK:{
			switch (touchEvent.state){
			case TouchEvent.TOUCH_STARTED:{
				debugTouchStarted(openGlTouch);
				break;
			}
			case TouchEvent.TOUCH_MOVING:{
				MyVector secondLast = touchEvent.getSecondLastTouchPoint().getOpenGlPosition(interactor.camera);
				//get touched gridfields
				Vector<GridField> gfs = getGridFieldLine(secondLast, openGlTouch);
				informTouchedGridField(gfs); //reduce friction
				//wipe Obstacles away
				wipeObstacles(gfs,touchEvent.getVelocity(false));
				break;
			}
			case TouchEvent.TOUCH_FINISHED:{
				debugTouchFinished();
				break;
			}
			default: Log.e(LOG_TAG, "Bad touch event state!");
			}
			break;
		}
		case GAMEPHASE_FINISH:{
			switch (touchEvent.state){
			case TouchEvent.TOUCH_STARTED:{
				debugTouchStarted(openGlTouch);
				break;
			}
			case TouchEvent.TOUCH_MOVING:{
				if(touchEvent.actuallyMoved()){
					MyVector secondLast = touchEvent.getSecondLastTouchPoint().getOpenGlPosition(interactor.camera);
					//get touched gridfields
					Vector<GridField> gfs = getGridFieldLine(secondLast, openGlTouch);
					informTouchedGridField(gfs);//reducing friction
					//wipe Obstacles away
					wipeObstacles(gfs,touchEvent.getVelocity(false));
					break;
				}
				break;
			}
			case TouchEvent.TOUCH_FINISHED:{
				break;
			}
			default: Log.e(LOG_TAG, "Bad touch event state!");
			}
		}
		break;
		}
	}

	public void handleSpinEvent(float xSpin){
		if(gamePhase == GAMEPHASE_RINK && pinguin.gliding){
			float spin2heightFactor = maximalJumpVelocity/570f;
			float upVelocity = xSpin * spin2heightFactor; //saturationFunction(xSpin * spin2heightFactor, maximalJumpHeight, -1);
			pinguin.setVelocity(1, Math.min(maximalJumpVelocity,upVelocity));
		}
	}

	public float saturationFunction(float value, float a, float b){
		return a * (1 - (float)Math.exp(b * value));
	}


	/**
	 * Wipe all moveable obstacles affected by given TouchEvent away.
	 * @param touchEvent
	 */
	private void wipeObstacles(Vector<GridField> touchedGridFields, MyVector wipeVel) {
		float factor =2f; //increase of actual Wipespeed 
		float xVel = factor*wipeVel.get(0)*visibleWidth/TIMER_INTERVAL;
		float zVel=  factor*wipeVel.get(1)*visibleLength/TIMER_INTERVAL;
		MyVector velocity=MyVector.makeVector3(xVel, 0,zVel); 

		for(GridField gf:touchedGridFields){
			for(Moveable m:gf.getMoveableObstacles()){

				Log.e(LOG_TAG, "speed "+velocity);
				m.setVelocity(m.getVelocity().add(velocity));
			}
		}
	}

	//Touch debugging methods

	/**
	 * Initializes vector of all touched GridFields. Should only be called when Touch started.
	 * @param openGlTouch
	 */
	private void debugTouchStarted(MyVector last){
		if(DEBUG_MODE){
			touchedGFs=new Vector<GridField>();
			informTouchedGridField(getGridFieldLine(last, last));
		}
	}
	/**
	 * Gives notice to all corresponding GridFields the "miniWipe" definded by the last two Touchpoints.
	 * @param openGlTouch
	 */
	private void informTouchedGridField(Vector<GridField> touchedGridFields){
		if(touchedGridFields.size()>1)touchedGridFields.remove(0); //else secondLast Coordinate would be informed twice
		for(GridField g:touchedGridFields){
			g.noticeTouched();
		}
		if(DEBUG_MODE)touchedGFs.addAll(touchedGridFields);
	}
	/**
	 * Sets color of all touched GridFields to previous color. Should only be called when Touch finished.
	 */
	private void debugTouchFinished(){
		if(DEBUG_MODE){
			for(GridField gf:touchedGFs){
				gf.noticeReleased();
			}
		}
	}



	public void handleTiltEvent(TiltEvent event){
		if(gamePhase == GAMEPHASE_RINK || gamePhase == GAMEPHASE_FINISH){
			//			Log.w(LOG_TAG,"Drifting-Force: "+event.getAcceleration());
			float driftInModelCoordsPerMSec=event.getAcceleration().get(0)/TiltEvent.maxAccelerationPerMsec;
			pinguin.setDrift(driftInModelCoordsPerMSec);
			//			Log.w(LOG_TAG,"Speed Tilt: "+ event.getAccSpeed());
		}
	}

	/**Fast access to GridField given a specified Point of the 3d Visualisation space  
	 * @return array-coordinates of GridField which listens to the specified Coordinates
	 */
	public MyVector coords2GridField(MyVector coordinates){

		float gridWidth=getGridFieldWidth();
		float cX=ApproximateShape.cutXBackToModelSpace(coordinates.get(0));
		float cZ=ApproximateShape.cutZBackToModelSpace(coordinates.get(2));

		//		if(cX<xModelMin||cX>xModelMax||cZ<-zModelMin||cZ>-zModelMax){
		//			Log.e(LOG_TAG,"coordinates are not covered by Gridfields: "+coordinates);
		//			return null;
		//		}
		//		Log.w(LOG_TAG, "X: "+Math.abs(cX-xModelMin)/gridWidth+" = "+(int)(Math.abs(cX-xModelMin)/gridWidth));
		//		Log.w(LOG_TAG, "Y: "+Math.abs(cZ-zModelMin)/gridWidth+" = "+(int)(Math.abs(cZ-zModelMin)/gridWidth));
		int gfXcoord= (int)(Math.abs(cX-xModelOrigin)/gridWidth);
		int gfZcoord= (int)(Math.abs(cZ-zModelOrigin)/gridWidth);
		return MyVector.makeVector2(gfXcoord,gfZcoord);

	}



	/**
	 * Bresenham-Algorithmus (see Wikipedia): gets Gridfields on the line between 2 openGl-coordinates
	 * @param openGlCoord1 Coordinates of starting point
	 * @param openGlCoord2 Coordinates of ending point
	 * @return all Gridfields on the line
	 */
	public Vector<GridField> getGridFieldLine(MyVector openGlCoord1,MyVector openGlCoord2){

		MyVector start=coords2GridField(openGlCoord1);
		MyVector end=coords2GridField(openGlCoord2);
		int xstart=(int) Math.min(grid.length-1,start.get(0));
		int ystart=(int) Math.min(grid[0].length-1,start.get(1));
		int xend=(int) Math.min(grid.length-1,end.get(0));
		int yend=(int) Math.min(grid[0].length-1,end.get(1));

		Vector<GridField> res= new Vector<GridField>();
		{
			int x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;

			/* Distances in GridField Coordinates */
			dx = xend - xstart;
			dy = yend - ystart;

			/* Sign of increment: dependent on quadrant */
			incx = (dx > 0) ? 1 : (dx < 0) ? -1 : 0;
			incy = (dy > 0) ? 1 : (dy < 0) ? -1 : 0;
			if(dx<0) dx = -dx;
			if(dy<0) dy = -dy;

			/* Which distance is larger */
			if (dx>dy)
			{
				/* X is dominant*/
				pdx=incx; pdy=0;    /* pd. ist Parallelschritt */
				ddx=incx; ddy=incy; /* dd. ist Diagonalschritt */
				es =dy;   el =dx;   /* Fehlerschritte schnell, langsam */
			} else
			{
				/* Y is dominant*/
				pdx=0;    pdy=incy; /* pd. ist Parallelschritt */
				ddx=incx; ddy=incy; /* dd. ist Diagonalschritt */
				es =dx;   el =dy;   /* Fehlerschritte schnell, langsam */
			}

			/*initialize */
			x = xstart;
			y = ystart;
			err = el/2;
			res.add(grid[x][y]);

			/* Pixel berechnen */
			for(t=0; t<el; ++t) /* t counts processed Gridfields, el is number of GridFields on the line */
			{
				/* update error*/
				err -= es; 
				if(err<0)
				{
					/*reset error */
					err += el;
					/* Step in less dominant direction*/
					x += ddx;
					y += ddy;
				} else
				{
					/* Step in dominant direction*/
					x += pdx;
					y += pdy;
				}
				res.add(grid[x][y]);
			}
		} 
		return res;
	}

	/**
	 * Recomputes all changeable values of all ModelObjects (e.g. next position of Moveables)
	 */
	public void recomputeModel() {
		if(!doRecompute) return;
		long begin=System.nanoTime();

		//remove objects which should be removed (e.g. due to fatal collision)
		objects.removeAll(objectsToRemove);
		objectsToRemove.clear();

		if(!SCENE_ROTATABLE)cameraController.moveCameraOnRail(Math.abs(pinguin.position.get(2)));

		//check if game has ended//			
		MyVector pinguinVel=pinguin.getVelocity();
		//		Log.e(LOG_TAG, "pinVel: "+pinguinVel);
		if(gamePhase!=GAMEPHASE_RUNWAY&&!firstRecompution){
			if(pinguinVel.getNorm()<minVelocity&&pinguin.gliding) endGameRun(false);
		}

		float durPosRecomp=0;
		float durScanTranslate=0;
		float durGridFieldSet=0;

		for(ModelObject mo : objects){
			if(mo instanceof Moveable){

				Moveable mmo = ((Moveable) mo);

				float beginPos = System.nanoTime();
				MyVector newPosition = mmo.computeNextPosition(TIMER_INTERVAL);
				MyVector newVel = mmo.recomputeVelocity(TIMER_INTERVAL); 
				mmo.setVelocity(newVel);
				//				Log.e(LOG_TAG, "Current velocity: " + mmo.getVelocity().get(2));

				if(newPosition.equals(mmo.position)) continue;
				durPosRecomp += (System.nanoTime()-beginPos)/1000000.0;

				// Shift already known scan data of object
				beginPos = System.nanoTime();
				MyVector posShift =newPosition.subtract( mmo.position);
				Vector<MyVector> scanData = mmo.translateScanData(posShift);
				durScanTranslate+= (System.nanoTime()-beginPos)/1000000.0;


				mmo.setPos(newPosition, Node.RESET);

				beginPos = System.nanoTime();
				occupyGridFields(scanData, mmo);
				durGridFieldSet += (System.nanoTime()-beginPos)/1000000.0;

				if(mmo.collisionOccured()) handleCollisions(mmo);
			}
		}

		enterGamePhase();

		firstRecompution=false;
		float duration = (System.nanoTime()-begin)/1000000.0f;
		if(duration > TIMER_INTERVAL-5) 
			Log.w(LOG_TAG,"Recompution-time: "+ duration+" Position: "+durPosRecomp
					+" Scan-Translation: "+durScanTranslate
					+" GridField Update: "+durGridFieldSet);


	}

	private void enterGamePhase(){

		int newGamePhase=pinguin.getGamephase();

		if(newGamePhase==GAMEPHASE_UNDEFINED)newGamePhase = GAMEPHASE_WATER;

		int oldGamePhase=gamePhase;
		if(oldGamePhase!=newGamePhase && newGamePhase!=GAMEPHASE_UNDEFINED){
			cameraController.setZoomStep(newGamePhase,true);
			if(newGamePhase==GAMEPHASE_FINISH){
				cameraController.lockCameraToRef(goal.getTargetPosition());
			}
			else cameraController.unlockAllCameraDimensions();
			if(newGamePhase==GAMEPHASE_RUNWAY){
				pinguin.setRotation(MyVector.makeVector3(90, 0, 0));
				pinguin.setHeadRotation(MyVector.makeVector3(-90, 0, 0));
				jukeBox.playBackGroundSound();
			}
			if(newGamePhase==GAMEPHASE_RINK){
				pinguin.setRotation(MyVector.makeVector3(0, 0, 0));
				pinguin.setHeadRotation(MyVector.makeVector3(0, 0, 0));
				jukeBox.playSlidingSound();
			}
		}
		gamePhase=newGamePhase;

	}

	/**
	 * Creates a constellation of ModelObjects forming the specified level
	 * @param level: number of level to be load
	 * @return ArrayList of ModelObjects representing the physical setting of the level
	 */
	private Vector<ModelObject> loadLevel(int level){
		objects = new Vector<ModelObject>();
		float friction=0.00015f;
		float iceThick = 15f;
		PlyFileReader reader= new PlyFileReader();

		if(level ==0){

			int textureRink = R.drawable.texture_ice15;
			int textureRunway = R.drawable.texture_wood3;
			int textureWater = R.drawable.texture_water;
			//Runway 
			Ground runway = new Ground(friction, GAMEPHASE_RUNWAY, MyVector.makeVector3(xModelRange*0.5f, 0f, 10f),Node.MIDDLE_LOW, xModelRange-18f, 20f, visibleLength, textureRunway, ModelObject.CONSITENCY_HARD_AS_ICE);

			// Pinguin
			MyVector pinguinStartPos = MyVector.makeVector3(xModelRange*0.5f, runway.getSize().get(1)+5f, 0);
			TriangleMesh torso = new TriangleMesh();
			TriangleMesh head = new TriangleMesh();
			reader.readPlyFile("PenguinRemeshed2.ply", torso,getApplicationContext());
			reader.readPlyFile("PenguinHeadRemeshed2.ply", head,getApplicationContext());
			torso.scaleToSize(15);
			head.scaleToSize(10);
			pinguin = new Pinguin(pinguinStartPos,Node.MIDDLE_LOW, MyVector.makeVector3(0,0,0),torso,head);

			float pinguinWidth = pinguin.getSize().get(0);
			float pinguinLength = pinguin.getSize().get(1);

			// Rink
			float endRunway = -runway.getSize().get(2);
			float rinkWidth = 0.8f*xModelRange;
			float holeXPos = 0.5f;
			float holeZPos = endRunway-1.5f*visibleLength;
			float holeLength = 2*pinguinLength;
			float[] iceTextSizePerDim = {pinguinLength, iceThick, -holeLength};  
			int holeZPosTextCoord = (int) (holeZPos/iceTextSizePerDim[2]+0.5);
			int rinkWidthInTextCoord = (int) (rinkWidth/iceTextSizePerDim[0]+0.5);

			int[] rink1numTextPerDim = {rinkWidthInTextCoord, 1, holeZPosTextCoord};
			Ground rink1 =  new Ground(friction, GAMEPHASE_RINK, MyVector.makeVector3(xModelRange*0.5f, -iceThick, 0f),Node.MIDDLE_LOW, iceTextSizePerDim, rink1numTextPerDim, textureRink, ModelObject.CONSITENCY_HARD_AS_ICE);

			float rinkWithoutHole =rink1.getSize().get(0)-2*pinguinWidth; 
			float borderWidth = (xModelRange-rink1.getSize().get(0))/2.0f;
			float endRink1 = -rink1.getSize().get(2);
			int rinkWithoutHoleTextCoords = (int) (rinkWithoutHole/iceTextSizePerDim[0]);

			int[] rinkLFHoleNumTexperDim = { (int)(rinkWithoutHoleTextCoords*holeXPos+0.5), 1 , 1 };
			int[] rinkRFHoleNumTexperDim= { (int)(rinkWithoutHoleTextCoords*(1-holeXPos)+0.5), 1 , 1 };
			Ground rinkLeftFromHole =  new Ground(friction, GAMEPHASE_RINK, MyVector.makeVector3(borderWidth, -iceThick, endRink1),Node.CORNER_LOWER_LEFT, iceTextSizePerDim, rinkLFHoleNumTexperDim , textureRink, ModelObject.CONSITENCY_HARD_AS_ICE);
			Ground rinkRightFromHole =  new Ground(friction, GAMEPHASE_RINK, MyVector.makeVector3(xModelRange-borderWidth, -iceThick, endRink1),Node.CORNER_LOWER_RIGHT,  iceTextSizePerDim, rinkRFHoleNumTexperDim, textureRink, ModelObject.CONSITENCY_HARD_AS_ICE);

			float endRinkLeftFromHole = endRink1-rinkLeftFromHole.getSize().get(2);

			int visibleLengthInTexCoord = (int) (visibleLength/iceTextSizePerDim[0]+0.5);
			int[] rink2NumTexPerDim ={rinkWidthInTextCoord, 1 , visibleLengthInTexCoord};
			Ground rink2 =  new Ground(friction, GAMEPHASE_RINK, MyVector.makeVector3(xModelRange*0.5f, -iceThick, endRinkLeftFromHole),Node.MIDDLE_LOW, iceTextSizePerDim,rink2NumTexPerDim, textureRink, ModelObject.CONSITENCY_HARD_AS_ICE);

			float endRink2 = endRinkLeftFromHole-rink2.getSize().get(2);

			int[] rink3NumTexPerDim ={ rinkWidthInTextCoord, 1, (int) (visibleLengthInTexCoord*0.5)};
			Ground rink3 =  new Ground(friction, GAMEPHASE_RINK, MyVector.makeVector3(xModelRange*0.5f, -iceThick, endRink2-2f*pinguinLength),Node.MIDDLE_LOW,iceTextSizePerDim, rink3NumTexPerDim, textureRink, ModelObject.CONSITENCY_HARD_AS_ICE);

			float endRink3 = endRink2-2*pinguinLength-rink3.getSize().get(2);

			//Goal
			TriangleMesh goalMesh = new TriangleMesh();
			reader.readPlyFile("tuna.ply", goalMesh, this.getApplicationContext());
			goalMesh.scaleToSize(20);
			goalMesh.rotate(-90, 30, 90, true);
			float[] iceGoalTextSizePerDim = {pinguinLength, iceThick, -visibleLength*Camera.HEIGHT_FINISH/Camera.HEIGHT_DEFAULT};  
			int[] goalNumTexPerDim ={rinkWidthInTextCoord, 1,1};
			goal=new Goal(friction,MyVector.makeVector3(xModelRange*0.5f, -iceThick, endRink3+1),Node.MIDDLE_LOW, iceGoalTextSizePerDim, goalNumTexPerDim, textureRink, MyVector.makeVector3(0.5f, 1f, 0.5f), goalMesh);
			float endGoal = endRink3-goal.getSize().get(2);
			zModelRange = Math.abs(endGoal)+pinguinLength*2;

			//Water
			float[] waterTextureSizePerDim = {(zModelRange+2*visibleLength)/17, 0.5f, (zModelRange+2*visibleLength)/17};
			int[] waterNumTexPerDim ={17,1,17};
			Water water = new Water(MyVector.makeVector3(xModelRange*0.5f, -20f, -zModelRange/2.0f),Node.CENTER,waterTextureSizePerDim,waterNumTexPerDim , textureWater);


			// Obstacles


			TriangleMesh obstmesh = new TriangleMesh();
			reader.readPlyFile("iglu.ply", obstmesh, this.getApplicationContext());
			obstmesh.scaleToSize(25);
			Snow obst1 = new Snow(obstmesh,MyVector.makeVector3(borderWidth,0, endRink1+pinguinLength),Node.CORNER_UPPER_LEFT, MyVector.makeVector3(0, -70, 0));

			TriangleMesh obstmesh2 = new TriangleMesh();
			reader.readPlyFile("obstacle4Remeshed.ply", obstmesh2, this.getApplicationContext());
			obstmesh2.scaleToSize(20);
			Snow obst2 = new Snow(obstmesh2,MyVector.makeVector3(xModelRange-borderWidth, 0, rink2.getCenter().get(2)),Node.CORNER_LOWER_RIGHT, MyVector.makeVector3(0, -70, 0));

			TriangleMesh obstmesh3 = new TriangleMesh();
			reader.readPlyFile("obstacle2Remeshed.ply", obstmesh3, this.getApplicationContext());
			obstmesh3.scaleToSize(20);
			Snow obst3 = new Snow(obstmesh3,MyVector.makeVector3(borderWidth, 0, endRink2),Node.CORNER_UPPER_LEFT, MyVector.makeVector3(0, 0 , 0));


			TriangleMesh obstmesh4 = new TriangleMesh();
			reader.readPlyFile("obstacleRemeshed.ply", obstmesh4, this.getApplicationContext());
			obstmesh4.scaleToSize(20);
			Snow obst4 = new Snow(obstmesh4,MyVector.makeVector3(xModelRange-borderWidth, 0, endRink2-holeLength*2),Node.CORNER_LOWER_RIGHT,MyVector.makeVector3(0, 0 , 0));


			TriangleMesh fishmesh = new TriangleMesh();
			reader.readPlyFile("fishbone.ply", fishmesh,getApplicationContext());
			fishmesh.scaleToSize(10);
			objects.add(new Fishbone(fishmesh,MyVector.makeVector3(xModelRange/2, 5, endRink2+pinguinLength),Node.MIDDLE_LOW, MyVector.makeVector3(0, 45, 0)));


			objects.add(pinguin);
			objects.add(runway);
			objects.add(rink1);
			objects.add(rinkLeftFromHole);
			objects.add(rinkRightFromHole);
			objects.add(rink2);
			objects.add(rink3);
			objects.add(goal);
			objects.add(water);


			objects.add(obst1);
			objects.add(obst2);
			objects.add(obst3);
			objects.add(obst4);

		}
		if(level == 1){
			int textureRink = R.drawable.texture_ice15;
			int textureRunway = R.drawable.texture_wood3;
			int textureWater = R.drawable.texture_water;
			//Runway 
			Ground runway = new Ground(friction, GAMEPHASE_RUNWAY, MyVector.makeVector3(xModelRange*0.5f, 0f, 10f),Node.MIDDLE_LOW, xModelRange-18f, 20f, visibleLength, textureRunway, ModelObject.CONSITENCY_HARD_AS_ICE);

			// Pinguin
			MyVector pinguinStartPos = MyVector.makeVector3(xModelRange*0.5f, runway.getSize().get(1)+5f, 0);

			//test
			TriangleMesh torso = new TriangleMesh();
			TriangleMesh head = new TriangleMesh();
			reader.readPlyFile("PenguinRemeshed2.ply", torso,getApplicationContext());
			reader.readPlyFile("PenguinHeadRemeshed2.ply", head,getApplicationContext());
			torso.scaleToSize(15);
			head.scaleToSize(10);
			pinguin = new Pinguin(pinguinStartPos,Node.MIDDLE_LOW, MyVector.makeVector3(0, 0, 0),torso,head);

			float pinguinWidth = pinguin.getSize().get(0);
			float pinguinLength = pinguin.getSize().get(1);

			// Rink
			float endRunway = -runway.getSize().get(2);
			float rinkWidth = 0.8f*xModelRange;
			float holeXPos = 0.5f;
			float holeZPos = endRunway-1.5f*visibleLength;
			float holeLength = 2*pinguinLength;
			float[] iceTextSizePerDim = {pinguinLength, iceThick, -holeLength};  
			int holeZPosTextCoord = (int) (holeZPos/iceTextSizePerDim[2]+0.5);
			int rinkWidthInTextCoord = (int) (rinkWidth/iceTextSizePerDim[0]+0.5);

			int[] rink1numTextPerDim = {rinkWidthInTextCoord, 1, holeZPosTextCoord};
			Ground rink1 =  new Ground(friction, GAMEPHASE_RINK, MyVector.makeVector3(xModelRange*0.5f, -iceThick, 0f),Node.MIDDLE_LOW, iceTextSizePerDim, rink1numTextPerDim, textureRink, ModelObject.CONSITENCY_HARD_AS_ICE);

			float rinkWithoutHole =rink1.getSize().get(0)-2*pinguinWidth; 
			float borderWidth = (xModelRange-rink1.getSize().get(0))/2.0f;
			float endRink1 = -rink1.getSize().get(2);
			int rinkWithoutHoleTextCoords = (int) (rinkWithoutHole/iceTextSizePerDim[0]);

			int[] rinkLFHoleNumTexperDim = { (int)(rinkWithoutHoleTextCoords*holeXPos+0.5), 1 , 2 };
			Ground rinkLeftFromHole =  new Ground(friction, GAMEPHASE_RINK, MyVector.makeVector3(borderWidth, -iceThick, endRink1),Node.CORNER_LOWER_LEFT, iceTextSizePerDim, rinkLFHoleNumTexperDim , textureRink, ModelObject.CONSITENCY_HARD_AS_ICE);

			float endRinkLeftFromHole = endRink1-rinkLeftFromHole.getSize().get(2);

			int visibleLengthInTexCoord = (int) (visibleLength/iceTextSizePerDim[0]+0.5);
			int[] rink2NumTexPerDim ={rinkWidthInTextCoord, 1 , visibleLengthInTexCoord};
			Ground rink2 =  new Ground(friction, GAMEPHASE_RINK, MyVector.makeVector3(xModelRange*0.5f, -iceThick, endRinkLeftFromHole),Node.MIDDLE_LOW, iceTextSizePerDim,rink2NumTexPerDim, textureRink, ModelObject.CONSITENCY_HARD_AS_ICE);

			float endRink2 = endRinkLeftFromHole-rink2.getSize().get(2);

			int[] rink3NumTexPerDim ={ rinkWidthInTextCoord, 1, (int) (visibleLengthInTexCoord*0.5)};
			Ground rink3 =  new Ground(friction, GAMEPHASE_RINK, MyVector.makeVector3(xModelRange*0.5f, -iceThick, endRink2-2f*pinguinLength),Node.MIDDLE_LOW,iceTextSizePerDim, rink3NumTexPerDim, textureRink, ModelObject.CONSITENCY_HARD_AS_ICE);

			float endRink3 = endRink2-2*pinguinLength-rink3.getSize().get(2);

			//Goal
			TriangleMesh goalMesh = new TriangleMesh();
			reader.readPlyFile("tuna.ply", goalMesh, this.getApplicationContext());
			goalMesh.scaleToSize(20);
			goalMesh.rotate(-90, 30, 90, true);
			float[] iceGoalTextSizePerDim = {pinguinLength, iceThick, -visibleLength*Camera.HEIGHT_FINISH/Camera.HEIGHT_DEFAULT};  
			int[] goalNumTexPerDim ={rinkWidthInTextCoord, 1,1};
			goal=new Goal(friction,MyVector.makeVector3(xModelRange*0.5f, -iceThick, endRink3+1),Node.MIDDLE_LOW, iceGoalTextSizePerDim, goalNumTexPerDim, textureRink, MyVector.makeVector3(0.5f, 1f, 0.5f), goalMesh);
			float endGoal = endRink3-goal.getSize().get(2);
			zModelRange = Math.abs(endGoal)+visibleLength*0.25f;

			//Water
			float[] waterTextureSizePerDim = {(zModelRange+2*visibleLength)/17, 0.5f, (zModelRange+2*visibleLength)/17};
			int[] waterNumTexPerDim ={17,1,17};
			Water water = new Water(MyVector.makeVector3(xModelRange*0.5f, -20f, -zModelRange/2.0f),Node.CENTER,waterTextureSizePerDim,waterNumTexPerDim , textureWater);

			// Obstacles


			TriangleMesh fishmesh1 = new TriangleMesh();
			reader.readPlyFile("fishbone.ply", fishmesh1, this.getApplicationContext());
			fishmesh1.scaleToSize(20);
			Fishbone fish1 = new Fishbone(fishmesh1,MyVector.makeVector3(borderWidth+5,5, endRink1-holeLength),Node.CORNER_LOWER_LEFT, MyVector.makeVector3(0, 35, 0));

			//			TriangleMesh obstmesh2 = new TriangleMesh();
			//			reader.readPlyFile("obstacle4Remeshed.ply", obstmesh2, this.getApplicationContext());
			//			obstmesh2.scaleToSize(20);
			//			Snow obst2 = new Snow(obstmesh2,MyVector.makeVector3(xModelRange-borderWidth, 0, endRinkLeftFromHole-pinguinLength),Node.CORNER_LOWER_RIGHT, MyVector.makeVector3(0, -70, 0));
			//	
			TriangleMesh obstmesh3 = new TriangleMesh();
			reader.readPlyFile("obstacle2Remeshed.ply", obstmesh3, this.getApplicationContext());
			obstmesh3.scaleToSize(20);
			Snow obst3 = new Snow(obstmesh3,MyVector.makeVector3(xModelRange/2-pinguinWidth, 0, rink2.position.get(2)),Node.CENTER, MyVector.makeVector3(0, 0 , 0));
			obst3.setPos(MyVector.makeVector3(0, obst3.getSize().get(1)/2, 0), true);


			TriangleMesh obstmesh4 = new TriangleMesh();
			reader.readPlyFile("fishbone.ply", obstmesh4, this.getApplicationContext());
			obstmesh4.scaleToSize(15);
			Fishbone obst4 = new Fishbone(obstmesh4,MyVector.makeVector3(xModelRange-borderWidth-10, 5, endRink2-holeLength*4),Node.CORNER_LOWER_RIGHT,MyVector.makeVector3(0, 90 , 0));

			TriangleMesh fishmesh3 = new TriangleMesh();
			reader.readPlyFile("fishbone.ply", fishmesh3, this.getApplicationContext());
			fishmesh3.scaleToSize(16);
			objects.add(new Fishbone(fishmesh3,MyVector.makeVector3(xModelRange/2, 5, endRink2-holeLength*2),Node.MIDDLE_LOW,MyVector.makeVector3(0, 120 , 0)));

			TriangleMesh fishmesh = new TriangleMesh();
			reader.readPlyFile("fishbone.ply", fishmesh,getApplicationContext());
			fishmesh.scaleToSize(10);
			objects.add(new Fishbone(fishmesh,MyVector.makeVector3(xModelRange/2, 5, endRink2+pinguinLength),Node.MIDDLE_LOW, MyVector.makeVector3(0, 45, 0)));

			TriangleMesh om2 = new TriangleMesh();
			reader.readPlyFile("obstacleRemeshed.ply", om2, this.getApplicationContext());
			om2.scaleToSize(20);
			objects.add(new Snow(om2,MyVector.makeVector3(xModelRange/2 - pinguinWidth,5, endRink3+pinguinLength),Node.MIDDLE_LOW, MyVector.makeVector3(0, 10, 0)));

			objects.add(pinguin);
			objects.add(runway);
			objects.add(rink1);
			objects.add(rinkLeftFromHole);
			objects.add(rink2);
			objects.add(rink3);
			objects.add(goal);
			objects.add(water);


			objects.add(fish1);
			//			objects.add(obst2);
			objects.add(obst3);
			objects.add(obst4);
		}


		// Create scene graph
		rootNode = new Node();
		for(ModelObject mo : objects){
			rootNode.addChild(mo.node);
		}
		rootNode.addChild(goal.getTargetNode());


		// Initialize Grid and insert ModelObjects into Grid
		initializeGrid();
		insertObjectsIntoGrid();


		return objects;
	}



	/**
	 * Initializes empty GridFields
	 */
	private void initializeGrid(){
		float gridFieldWidth=getGridFieldWidth();
		int xRange= (int)(0.5+Math.abs(xModelRange-xModelOrigin)/gridFieldWidth);
		int zRange= (int)(0.5+Math.abs(zModelRange-zModelOrigin)/gridFieldWidth);
		grid = new GridField[xRange][zRange];
		Node debugNode=new Node();
		for(int x=0; x<xRange;x++){
			for(int z=0; z<zRange; z++){
				float xc=x*gridFieldWidth+0.5f*gridFieldWidth;
				float yc=0.5f; //TODO maybe better value like height of ground
				float zc=-(z*gridFieldWidth+0.5f*gridFieldWidth);
				MyVector center=MyVector.makeVector3(xc, yc, zc);
				//				Log.i(LOG_TAG,"Center of GridField "+x+","+z+": "+center+" width: ");
				GridField gf= new GridField(center);
				grid[x][z]=gf;
				if(DEBUG_MODE) debugNode.addChild(gf.getDebugCube());
			}
		}
		if(DEBUG_MODE) rootNode.addChild(debugNode);
	}

	/**
	 * Inserts all ModelObjects into Grid
	 */
	private void insertObjectsIntoGrid(){
		float gridWidth = getGridFieldWidth();
		for(ModelObject o: objects){
			Vector<MyVector> scan = o.scanApproximateShape(gridWidth); //get scans of all objects
			occupyGridFields(scan, o); //occupies all Gridfields the object fills out (given by the scan)
		}
	}

	/**
	 * Adds o to all GridFields which are affected by the scanData
	 * @param scan
	 * @param o
	 */
	private void occupyGridFields(Vector<MyVector> scanData, ModelObject o){
		if(o instanceof Moveable) ((Moveable) o).removeFromGridFields();

		for(int i = 0; i < scanData.size(); i += 2){

			Vector<GridField> gridFields = getGridFieldLine(scanData.get(i), scanData.get(i+1)); //get corresponding GridFields
			for(GridField gf:gridFields){
				gf.addOccupant(o);//gives notice to corresponding GridFields
			}
		}
	}


	public static float getGridFieldWidth() {
		return Math.abs(xModelRange-xModelOrigin)/numGridFields;
	}

	private int readHighscore() {
		SharedPreferences pref = getSharedPreferences("GAME", 0);
		return pref.getInt("HIGHSCORE_LEVEL"+level, 0);
	}

	private void writeHighscore(int highscore) {
		SharedPreferences pref = getSharedPreferences("GAME", 0);
		SharedPreferences.Editor editor = pref.edit();
		editor.putInt("HIGHSCORE_LEVEL"+level, highscore);
		editor.commit();
	}

	private void compareAndWriteHighscore(int highscore){
		if(highscore>readHighscore()) writeHighscore(highscore);
	}


	/* (non-Javadoc)
	 * @see android.app.Activity#onPause()
	 */
	@Override
	protected void onPause() {
		super.onPause();
		doRecompute=false;
		//		interactor.onPause();
		Log.e(LOG_TAG, "OnPause");
		jukeBox.pauseBackGroundSound();
		jukeBox.pauseSlidingSound();

	}





	/* (non-Javadoc)
	 * @see android.app.Activity#onResume()
	 */
	@Override
	protected void onResume() {
		super.onResume();
		//		interactor.onResume();
		Log.e(LOG_TAG, "OnResume");
		jukeBox.playBackGroundSound();
		if(!firstInitialization){
			gamePhase=GAMEPHASE_UNDEFINED;
			enterGamePhase();
		}
		else firstInitialization=false;
		doRecompute=true;
	}



	/* (non-Javadoc)
	 * @see android.app.Activity#onDestroy()
	 */
	@Override
	protected void onDestroy() {
		super.onDestroy();
		Log.e(LOG_TAG, "OnDestroy");
		timer.cancel();
		timer.purge();
		timer=null;
		interactor.onPause();
		interactor.destroy();
		rootNode.destroy();
		rootNode=null;
		for(GridField[] grids:grid){
			for(GridField gf : grids){
				gf.destroy();
				gf=null;
			}
			grids=null;
		}
		grid=null;	
		dialog.dismiss();
		dialog=null;
		jukeBox.releaseAllSounds();
		jukeBox = null;
		tiltInterpreter.destroy();
		tiltInterpreter=null;
		spinInterpreter.destroy();
		spinInterpreter=null;
		objects.clear();
		objects = null;
		grid =null;
		goal=null;
		pinguin=null;
		System.gc();
	}


	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		MenuInflater menuInflater = getMenuInflater();
		menuInflater.inflate(R.layout.menu, menu);
		return true;
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		onPause();
		return super.onPrepareOptionsMenu(menu);
	}
	/**
	 * Event Handling for Individual menu item selected
	 * Identify single menu item by it's id
	 * */
	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		Log.w(LOG_TAG,"itemId "+(item.getItemId()==R.id.menu_resume));
		switch (item.getItemId())
		{
		case R.id.menu_resume:{
			resume=true;
			Toast.makeText(Controller.this, "Resume", Toast.LENGTH_SHORT).show();
			return true;
		}
		case R.id.menu_restart:{
			resume=false;
			restartButton.performClick();
			return true;
		}
		//		case R.id.menu_mute:
		//			// Single menu item is selected do something
		//			// Ex: launching new activity/screen or show alert message
		//			String message = "Mute";
		//			if(jukeBox.isMute()) jukeBox.muteAllSounds();
		//			else{
		//				jukeBox.resetSoundVolumes() ;
		//				message ="Unmute";
		//			}
		//			Toast.makeText(Controller.this, message, Toast.LENGTH_SHORT).show();
		//			return true;

		case R.id.menu_exit:{
			resume=false;
			menuButton.performClick();
			return true;
		}

		default:

			return super.onOptionsItemSelected(item);
		}

	}  

	@Override
	public void onOptionsMenuClosed(Menu menu) {
		// TODO Auto-generated method stub
		if(resume) onResume();
		else resume=true;
		super.onOptionsMenuClosed(menu);
	}


	@Override
	public void onClick(View v) {
		if(v.getId()==R.id.bRestart) {//user wants to restart
			dialog.dismiss();
			Intent newGameScreen= new Intent(getApplicationContext(),Controller.class);
			newGameScreen.putExtra("levelNumber", level);
			this.finish();
			startActivity(newGameScreen);	

		}
		if(v.getId()==R.id.bMainMenu){//user wants to exit to Main menu
			dialog.dismiss();
			Intent newMenuScreen= new Intent(getApplicationContext(), MenuScreen.class);
			startActivity(newMenuScreen);
			this.finish();
		}

	}

	@Override
	public void onBackPressed() {
		openOptionsMenu();
	}










}
